{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "feature_crosses.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "collapsed_sections": [
        "ZTDHHM61NPTw",
        "0i7vGo9PTaZl",
        "copyright-notice"
      ]
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "copyright-notice",
        "colab_type": "text"
      },
      "source": [
        "#### Copyright 2017 Google LLC."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "both",
        "id": "copyright-notice2",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "g4T-_IsVbweU",
        "colab_type": "text"
      },
      "source": [
        " # 特征组合"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "F7dke6skIK-k",
        "colab_type": "text"
      },
      "source": [
        " **学习目标：**\n",
        "  * 通过添加其他合成特征来改进线性回归模型（这是前一个练习的延续）\n",
        "  * 使用输入函数将 Pandas `DataFrame` 对象转换为 `Tensors`，并在 `fit()` 和 `predict()` 中调用输入函数\n",
        "  * 使用 FTRL 优化算法进行模型训练\n",
        "  * 通过独热编码、分箱和特征组合创建新的合成特征"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NS_fcQRd8B97",
        "colab_type": "text"
      },
      "source": [
        " ## 设置"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4IdzD8IdIK-l",
        "colab_type": "text"
      },
      "source": [
        " 首先，我们来定义输入并创建数据加载代码，正如我们在之前的练习中所做的那样。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "CsfdiLiDIK-n",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "from __future__ import print_function\n",
        "\n",
        "import math\n",
        "\n",
        "from IPython import display\n",
        "from matplotlib import cm\n",
        "from matplotlib import gridspec\n",
        "from matplotlib import pyplot as plt\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "from sklearn import metrics\n",
        "import tensorflow as tf\n",
        "from tensorflow.python.data import Dataset\n",
        "\n",
        "tf.logging.set_verbosity(tf.logging.ERROR)\n",
        "pd.options.display.max_rows = 10\n",
        "pd.options.display.float_format = '{:.1f}'.format\n",
        "\n",
        "california_housing_dataframe = pd.read_csv(\"https://download.mlcc.google.cn/mledu-datasets/california_housing_train.csv\", sep=\",\")\n",
        "\n",
        "california_housing_dataframe = california_housing_dataframe.reindex(\n",
        "    np.random.permutation(california_housing_dataframe.index))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "10rhoflKIK-s",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def preprocess_features(california_housing_dataframe):\n",
        "  \"\"\"Prepares input features from California housing data set.\n",
        "\n",
        "  Args:\n",
        "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
        "      from the California housing data set.\n",
        "  Returns:\n",
        "    A DataFrame that contains the features to be used for the model, including\n",
        "    synthetic features.\n",
        "  \"\"\"\n",
        "  selected_features = california_housing_dataframe[\n",
        "    [\"latitude\",\n",
        "     \"longitude\",\n",
        "     \"housing_median_age\",\n",
        "     \"total_rooms\",\n",
        "     \"total_bedrooms\",\n",
        "     \"population\",\n",
        "     \"households\",\n",
        "     \"median_income\"]]\n",
        "  processed_features = selected_features.copy()\n",
        "  # Create a synthetic feature.\n",
        "  processed_features[\"rooms_per_person\"] = (\n",
        "    california_housing_dataframe[\"total_rooms\"] /\n",
        "    california_housing_dataframe[\"population\"])\n",
        "  return processed_features\n",
        "\n",
        "def preprocess_targets(california_housing_dataframe):\n",
        "  \"\"\"Prepares target features (i.e., labels) from California housing data set.\n",
        "\n",
        "  Args:\n",
        "    california_housing_dataframe: A Pandas DataFrame expected to contain data\n",
        "      from the California housing data set.\n",
        "  Returns:\n",
        "    A DataFrame that contains the target feature.\n",
        "  \"\"\"\n",
        "  output_targets = pd.DataFrame()\n",
        "  # Scale the target to be in units of thousands of dollars.\n",
        "  output_targets[\"median_house_value\"] = (\n",
        "    california_housing_dataframe[\"median_house_value\"] / 1000.0)\n",
        "  return output_targets"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ufplEkjN8KUp",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1209
        },
        "outputId": "7c4eea2f-8813-4173-d94b-7e88c9b5a49b"
      },
      "source": [
        "# Choose the first 12000 (out of 17000) examples for training.\n",
        "training_examples = preprocess_features(california_housing_dataframe.head(12000))\n",
        "training_targets = preprocess_targets(california_housing_dataframe.head(12000))\n",
        "\n",
        "# Choose the last 5000 (out of 17000) examples for validation.\n",
        "validation_examples = preprocess_features(california_housing_dataframe.tail(5000))\n",
        "validation_targets = preprocess_targets(california_housing_dataframe.tail(5000))\n",
        "\n",
        "# Double-check that we've done the right thing.\n",
        "print(\"Training examples summary:\")\n",
        "display.display(training_examples.describe())\n",
        "print(\"Validation examples summary:\")\n",
        "display.display(validation_examples.describe())\n",
        "\n",
        "print(\"Training targets summary:\")\n",
        "display.display(training_targets.describe())\n",
        "print(\"Validation targets summary:\")\n",
        "display.display(validation_targets.describe())"
      ],
      "execution_count": 3,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Training examples summary:\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>latitude</th>\n",
              "      <th>longitude</th>\n",
              "      <th>housing_median_age</th>\n",
              "      <th>total_rooms</th>\n",
              "      <th>total_bedrooms</th>\n",
              "      <th>population</th>\n",
              "      <th>households</th>\n",
              "      <th>median_income</th>\n",
              "      <th>rooms_per_person</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>count</th>\n",
              "      <td>12000.0</td>\n",
              "      <td>12000.0</td>\n",
              "      <td>12000.0</td>\n",
              "      <td>12000.0</td>\n",
              "      <td>12000.0</td>\n",
              "      <td>12000.0</td>\n",
              "      <td>12000.0</td>\n",
              "      <td>12000.0</td>\n",
              "      <td>12000.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>mean</th>\n",
              "      <td>35.6</td>\n",
              "      <td>-119.6</td>\n",
              "      <td>28.7</td>\n",
              "      <td>2635.2</td>\n",
              "      <td>538.5</td>\n",
              "      <td>1428.2</td>\n",
              "      <td>500.2</td>\n",
              "      <td>3.9</td>\n",
              "      <td>2.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>std</th>\n",
              "      <td>2.1</td>\n",
              "      <td>2.0</td>\n",
              "      <td>12.6</td>\n",
              "      <td>2144.0</td>\n",
              "      <td>416.1</td>\n",
              "      <td>1103.9</td>\n",
              "      <td>379.7</td>\n",
              "      <td>1.9</td>\n",
              "      <td>1.2</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>min</th>\n",
              "      <td>32.5</td>\n",
              "      <td>-124.3</td>\n",
              "      <td>1.0</td>\n",
              "      <td>2.0</td>\n",
              "      <td>1.0</td>\n",
              "      <td>3.0</td>\n",
              "      <td>1.0</td>\n",
              "      <td>0.5</td>\n",
              "      <td>0.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>25%</th>\n",
              "      <td>33.9</td>\n",
              "      <td>-121.8</td>\n",
              "      <td>18.0</td>\n",
              "      <td>1462.0</td>\n",
              "      <td>298.0</td>\n",
              "      <td>791.0</td>\n",
              "      <td>282.0</td>\n",
              "      <td>2.6</td>\n",
              "      <td>1.5</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>50%</th>\n",
              "      <td>34.2</td>\n",
              "      <td>-118.5</td>\n",
              "      <td>29.0</td>\n",
              "      <td>2122.0</td>\n",
              "      <td>433.0</td>\n",
              "      <td>1167.5</td>\n",
              "      <td>408.5</td>\n",
              "      <td>3.5</td>\n",
              "      <td>1.9</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>75%</th>\n",
              "      <td>37.7</td>\n",
              "      <td>-118.0</td>\n",
              "      <td>37.0</td>\n",
              "      <td>3155.0</td>\n",
              "      <td>647.0</td>\n",
              "      <td>1720.2</td>\n",
              "      <td>603.0</td>\n",
              "      <td>4.8</td>\n",
              "      <td>2.3</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>max</th>\n",
              "      <td>42.0</td>\n",
              "      <td>-114.3</td>\n",
              "      <td>52.0</td>\n",
              "      <td>32054.0</td>\n",
              "      <td>5290.0</td>\n",
              "      <td>15507.0</td>\n",
              "      <td>5050.0</td>\n",
              "      <td>15.0</td>\n",
              "      <td>55.2</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "       latitude  longitude  ...  median_income  rooms_per_person\n",
              "count   12000.0    12000.0  ...        12000.0           12000.0\n",
              "mean       35.6     -119.6  ...            3.9               2.0\n",
              "std         2.1        2.0  ...            1.9               1.2\n",
              "min        32.5     -124.3  ...            0.5               0.0\n",
              "25%        33.9     -121.8  ...            2.6               1.5\n",
              "50%        34.2     -118.5  ...            3.5               1.9\n",
              "75%        37.7     -118.0  ...            4.8               2.3\n",
              "max        42.0     -114.3  ...           15.0              55.2\n",
              "\n",
              "[8 rows x 9 columns]"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "Validation examples summary:\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>latitude</th>\n",
              "      <th>longitude</th>\n",
              "      <th>housing_median_age</th>\n",
              "      <th>total_rooms</th>\n",
              "      <th>total_bedrooms</th>\n",
              "      <th>population</th>\n",
              "      <th>households</th>\n",
              "      <th>median_income</th>\n",
              "      <th>rooms_per_person</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>count</th>\n",
              "      <td>5000.0</td>\n",
              "      <td>5000.0</td>\n",
              "      <td>5000.0</td>\n",
              "      <td>5000.0</td>\n",
              "      <td>5000.0</td>\n",
              "      <td>5000.0</td>\n",
              "      <td>5000.0</td>\n",
              "      <td>5000.0</td>\n",
              "      <td>5000.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>mean</th>\n",
              "      <td>35.6</td>\n",
              "      <td>-119.6</td>\n",
              "      <td>28.2</td>\n",
              "      <td>2663.9</td>\n",
              "      <td>541.6</td>\n",
              "      <td>1432.9</td>\n",
              "      <td>503.8</td>\n",
              "      <td>3.9</td>\n",
              "      <td>2.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>std</th>\n",
              "      <td>2.1</td>\n",
              "      <td>2.0</td>\n",
              "      <td>12.6</td>\n",
              "      <td>2264.1</td>\n",
              "      <td>434.2</td>\n",
              "      <td>1247.1</td>\n",
              "      <td>395.9</td>\n",
              "      <td>1.9</td>\n",
              "      <td>1.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>min</th>\n",
              "      <td>32.5</td>\n",
              "      <td>-124.3</td>\n",
              "      <td>2.0</td>\n",
              "      <td>15.0</td>\n",
              "      <td>3.0</td>\n",
              "      <td>11.0</td>\n",
              "      <td>2.0</td>\n",
              "      <td>0.5</td>\n",
              "      <td>0.1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>25%</th>\n",
              "      <td>33.9</td>\n",
              "      <td>-121.8</td>\n",
              "      <td>18.0</td>\n",
              "      <td>1462.0</td>\n",
              "      <td>294.0</td>\n",
              "      <td>787.0</td>\n",
              "      <td>280.8</td>\n",
              "      <td>2.6</td>\n",
              "      <td>1.5</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>50%</th>\n",
              "      <td>34.3</td>\n",
              "      <td>-118.5</td>\n",
              "      <td>28.0</td>\n",
              "      <td>2142.0</td>\n",
              "      <td>436.0</td>\n",
              "      <td>1165.5</td>\n",
              "      <td>410.0</td>\n",
              "      <td>3.5</td>\n",
              "      <td>1.9</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>75%</th>\n",
              "      <td>37.7</td>\n",
              "      <td>-118.0</td>\n",
              "      <td>37.0</td>\n",
              "      <td>3138.2</td>\n",
              "      <td>652.0</td>\n",
              "      <td>1722.2</td>\n",
              "      <td>608.0</td>\n",
              "      <td>4.8</td>\n",
              "      <td>2.3</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>max</th>\n",
              "      <td>41.9</td>\n",
              "      <td>-114.5</td>\n",
              "      <td>52.0</td>\n",
              "      <td>37937.0</td>\n",
              "      <td>6445.0</td>\n",
              "      <td>35682.0</td>\n",
              "      <td>6082.0</td>\n",
              "      <td>15.0</td>\n",
              "      <td>41.3</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "       latitude  longitude  ...  median_income  rooms_per_person\n",
              "count    5000.0     5000.0  ...         5000.0            5000.0\n",
              "mean       35.6     -119.6  ...            3.9               2.0\n",
              "std         2.1        2.0  ...            1.9               1.0\n",
              "min        32.5     -124.3  ...            0.5               0.1\n",
              "25%        33.9     -121.8  ...            2.6               1.5\n",
              "50%        34.3     -118.5  ...            3.5               1.9\n",
              "75%        37.7     -118.0  ...            4.8               2.3\n",
              "max        41.9     -114.5  ...           15.0              41.3\n",
              "\n",
              "[8 rows x 9 columns]"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "Training targets summary:\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>median_house_value</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>count</th>\n",
              "      <td>12000.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>mean</th>\n",
              "      <td>208.1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>std</th>\n",
              "      <td>116.7</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>min</th>\n",
              "      <td>15.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>25%</th>\n",
              "      <td>120.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>50%</th>\n",
              "      <td>181.1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>75%</th>\n",
              "      <td>265.8</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>max</th>\n",
              "      <td>500.0</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "       median_house_value\n",
              "count             12000.0\n",
              "mean                208.1\n",
              "std                 116.7\n",
              "min                  15.0\n",
              "25%                 120.0\n",
              "50%                 181.1\n",
              "75%                 265.8\n",
              "max                 500.0"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "Validation targets summary:\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>median_house_value</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>count</th>\n",
              "      <td>5000.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>mean</th>\n",
              "      <td>205.3</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>std</th>\n",
              "      <td>114.2</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>min</th>\n",
              "      <td>15.0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>25%</th>\n",
              "      <td>118.8</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>50%</th>\n",
              "      <td>178.4</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>75%</th>\n",
              "      <td>263.1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>max</th>\n",
              "      <td>500.0</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "       median_house_value\n",
              "count              5000.0\n",
              "mean                205.3\n",
              "std                 114.2\n",
              "min                  15.0\n",
              "25%                 118.8\n",
              "50%                 178.4\n",
              "75%                 263.1\n",
              "max                 500.0"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "oJlrB4rJ_2Ma",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def construct_feature_columns(input_features):\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Args:\n",
        "    input_features: The names of the numerical input features to use.\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\"\n",
        "  return set([tf.feature_column.numeric_column(my_feature)\n",
        "              for my_feature in input_features])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "NBxoAfp2AcB6",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def my_input_fn(features, targets, batch_size=1, shuffle=True, num_epochs=None):\n",
        "    \"\"\"Trains a linear regression model.\n",
        "  \n",
        "    Args:\n",
        "      features: pandas DataFrame of features\n",
        "      targets: pandas DataFrame of targets\n",
        "      batch_size: Size of batches to be passed to the model\n",
        "      shuffle: True or False. Whether to shuffle the data.\n",
        "      num_epochs: Number of epochs for which data should be repeated. None = repeat indefinitely\n",
        "    Returns:\n",
        "      Tuple of (features, labels) for next data batch\n",
        "    \"\"\"\n",
        "    \n",
        "    # Convert pandas data into a dict of np arrays.\n",
        "    features = {key:np.array(value) for key,value in dict(features).items()}                                           \n",
        " \n",
        "    # Construct a dataset, and configure batching/repeating.\n",
        "    ds = Dataset.from_tensor_slices((features,targets)) # warning: 2GB limit\n",
        "    ds = ds.batch(batch_size).repeat(num_epochs)\n",
        "    \n",
        "    # Shuffle the data, if specified.\n",
        "    if shuffle:\n",
        "      ds = ds.shuffle(10000)\n",
        "    \n",
        "    # Return the next batch of data.\n",
        "    features, labels = ds.make_one_shot_iterator().get_next()\n",
        "    return features, labels"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hweDyy31LBsV",
        "colab_type": "text"
      },
      "source": [
        " ## FTRL 优化算法\n",
        "\n",
        "高维度线性模型可受益于使用一种基于梯度的优化方法，叫做 FTRL。该算法的优势是针对不同系数以不同方式调整学习速率，如果某些特征很少采用非零值，该算法可能比较实用（也非常适合支持 L1 正则化）。我们可以使用 [FtrlOptimizer](https://www.tensorflow.org/api_docs/python/tf/train/FtrlOptimizer) 来应用 FTRL。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "S0SBf1X1IK_O",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def train_model(\n",
        "    learning_rate,\n",
        "    steps,\n",
        "    batch_size,\n",
        "    feature_columns,\n",
        "    training_examples,\n",
        "    training_targets,\n",
        "    validation_examples,\n",
        "    validation_targets):\n",
        "  \"\"\"Trains a linear regression model.\n",
        "  \n",
        "  In addition to training, this function also prints training progress information,\n",
        "  as well as a plot of the training and validation loss over time.\n",
        "  \n",
        "  Args:\n",
        "    learning_rate: A `float`, the learning rate.\n",
        "    steps: A non-zero `int`, the total number of training steps. A training step\n",
        "      consists of a forward and backward pass using a single batch.\n",
        "    feature_columns: A `set` specifying the input feature columns to use.\n",
        "    training_examples: A `DataFrame` containing one or more columns from\n",
        "      `california_housing_dataframe` to use as input features for training.\n",
        "    training_targets: A `DataFrame` containing exactly one column from\n",
        "      `california_housing_dataframe` to use as target for training.\n",
        "    validation_examples: A `DataFrame` containing one or more columns from\n",
        "      `california_housing_dataframe` to use as input features for validation.\n",
        "    validation_targets: A `DataFrame` containing exactly one column from\n",
        "      `california_housing_dataframe` to use as target for validation.\n",
        "      \n",
        "  Returns:\n",
        "    A `LinearRegressor` object trained on the training data.\n",
        "  \"\"\"\n",
        "\n",
        "  periods = 10\n",
        "  steps_per_period = steps / periods\n",
        "\n",
        "  # Create a linear regressor object.\n",
        "  my_optimizer = tf.train.FtrlOptimizer(learning_rate=learning_rate)\n",
        "  my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)\n",
        "  linear_regressor = tf.estimator.LinearRegressor(\n",
        "      feature_columns=feature_columns,\n",
        "      optimizer=my_optimizer\n",
        "  )\n",
        "  \n",
        "  training_input_fn = lambda: my_input_fn(training_examples, \n",
        "                                          training_targets[\"median_house_value\"], \n",
        "                                          batch_size=batch_size)\n",
        "  predict_training_input_fn = lambda: my_input_fn(training_examples, \n",
        "                                                  training_targets[\"median_house_value\"], \n",
        "                                                  num_epochs=1, \n",
        "                                                  shuffle=False)\n",
        "  predict_validation_input_fn = lambda: my_input_fn(validation_examples, \n",
        "                                                    validation_targets[\"median_house_value\"], \n",
        "                                                    num_epochs=1, \n",
        "                                                    shuffle=False)\n",
        "\n",
        "  # Train the model, but do so inside a loop so that we can periodically assess\n",
        "  # loss metrics.\n",
        "  print(\"Training model...\")\n",
        "  print(\"RMSE (on training data):\")\n",
        "  training_rmse = []\n",
        "  validation_rmse = []\n",
        "  for period in range (0, periods):\n",
        "    # Train the model, starting from the prior state.\n",
        "    linear_regressor.train(\n",
        "        input_fn=training_input_fn,\n",
        "        steps=steps_per_period\n",
        "    )\n",
        "    # Take a break and compute predictions.\n",
        "    training_predictions = linear_regressor.predict(input_fn=predict_training_input_fn)\n",
        "    training_predictions = np.array([item['predictions'][0] for item in training_predictions])\n",
        "    validation_predictions = linear_regressor.predict(input_fn=predict_validation_input_fn)\n",
        "    validation_predictions = np.array([item['predictions'][0] for item in validation_predictions])\n",
        "    \n",
        "    # Compute training and validation loss.\n",
        "    training_root_mean_squared_error = math.sqrt(\n",
        "        metrics.mean_squared_error(training_predictions, training_targets))\n",
        "    validation_root_mean_squared_error = math.sqrt(\n",
        "        metrics.mean_squared_error(validation_predictions, validation_targets))\n",
        "    # Occasionally print the current loss.\n",
        "    print(\"  period %02d : %0.2f\" % (period, training_root_mean_squared_error))\n",
        "    # Add the loss metrics from this period to our list.\n",
        "    training_rmse.append(training_root_mean_squared_error)\n",
        "    validation_rmse.append(validation_root_mean_squared_error)\n",
        "  print(\"Model training finished.\")\n",
        "\n",
        "  \n",
        "  # Output a graph of loss metrics over periods.\n",
        "  plt.ylabel(\"RMSE\")\n",
        "  plt.xlabel(\"Periods\")\n",
        "  plt.title(\"Root Mean Squared Error vs. Periods\")\n",
        "  plt.tight_layout()\n",
        "  plt.plot(training_rmse, label=\"training\")\n",
        "  plt.plot(validation_rmse, label=\"validation\")\n",
        "  plt.legend()\n",
        "\n",
        "  return linear_regressor"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "1Cdr02tLIK_Q",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 657
        },
        "outputId": "b92380ef-3a31-4550-c245-96978a61b831"
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(training_examples),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "execution_count": 7,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "\n",
            "WARNING: The TensorFlow contrib module will not be included in TensorFlow 2.0.\n",
            "For more information, please see:\n",
            "  * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md\n",
            "  * https://github.com/tensorflow/addons\n",
            "If you depend on functionality not listed there, please file an issue.\n",
            "\n",
            "Training model...\n",
            "RMSE (on training data):\n",
            "  period 00 : 210.89\n",
            "  period 01 : 133.55\n",
            "  period 02 : 170.90\n",
            "  period 03 : 121.73\n",
            "  period 04 : 130.09\n",
            "  period 05 : 123.92\n",
            "  period 06 : 131.78\n",
            "  period 07 : 107.94\n",
            "  period 08 : 138.06\n",
            "  period 09 : 144.73\n",
            "Model training finished.\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nAABJRU5ErkJggg==\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "i4lGvqajDWlw",
        "colab_type": "text"
      },
      "source": [
        " ## 离散特征的独热编码\n",
        "\n",
        "通常，在训练逻辑回归模型之前，离散（即字符串、枚举、整数）特征会转换为二元特征系列。\n",
        "\n",
        "例如，假设我们创建了一个合成特征，可以采用 `0`、`1` 或 `2` 中的任何值，并且我们还具有以下几个训练点：\n",
        "\n",
        "| # | feature_value |\n",
        "|---|---------------|\n",
        "| 0 |             2 |\n",
        "| 1 |             0 |\n",
        "| 2 |             1 |\n",
        "\n",
        "对于每个可能的分类值，我们都会创建一个新的**二元****实值**特征，该特征只能采用两个可能值中的一个：如果示例中包含该值，则值为 1.0；如果不包含，则值为 0.0。在上述示例中，分类特征会被转换成三个特征，现在训练点如下所示：\n",
        "\n",
        "| # | feature_value_0 | feature_value_1 | feature_value_2 |\n",
        "|---|-----------------|-----------------|-----------------|\n",
        "| 0 |             0.0 |             0.0 |             1.0 |\n",
        "| 1 |             1.0 |             0.0 |             0.0 |\n",
        "| 2 |             0.0 |             1.0 |             0.0 |"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KnssXowblKm7",
        "colab_type": "text"
      },
      "source": [
        " ## 分桶（分箱）特征\n",
        "\n",
        "分桶也称为分箱。\n",
        "\n",
        "例如，我们可以将 `population` 分为以下 3 个分桶：\n",
        "- `bucket_0` (`< 5000`)：对应于人口分布较少的街区\n",
        "- `bucket_1` (`5000 - 25000`)：对应于人口分布适中的街区\n",
        "- `bucket_2` (`> 25000`)：对应于人口分布较多的街区\n",
        "\n",
        "根据前面的分桶定义，以下 `population` 矢量：\n",
        "\n",
        "    [[10001], [42004], [2500], [18000]]\n",
        "\n",
        "将变成以下经过分桶的特征矢量：\n",
        "\n",
        "    [[1], [2], [0], [1]]\n",
        "\n",
        "这些特征值现在是分桶索引。请注意，这些索引被视为离散特征。通常情况下，这些特征将被进一步转换为上述独热表示法，但这是以透明方式实现的。\n",
        "\n",
        "要为分桶特征定义特征列，我们可以使用 [`bucketized_column`](https://www.tensorflow.org/api_docs/python/tf/feature_column/bucketized_column)（而不是使用 `numeric_column`），该列将数字列作为输入，并使用 `boundaries` 参数中指定的分桶边界将其转换为分桶特征。以下代码为 `households` 和 `longitude` 定义了分桶特征列；`get_quantile_based_boundaries` 函数会根据分位数计算边界，以便每个分桶包含相同数量的元素。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "cc9qZrtRy-ED",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_quantile_based_boundaries(feature_values, num_buckets):\n",
        "  boundaries = np.arange(1.0, num_buckets) / num_buckets\n",
        "  quantiles = feature_values.quantile(boundaries)\n",
        "  return [quantiles[q] for q in quantiles.keys()]\n",
        "\n",
        "# Divide households into 7 buckets.\n",
        "households = tf.feature_column.numeric_column(\"households\")\n",
        "bucketized_households = tf.feature_column.bucketized_column(\n",
        "  households, boundaries=get_quantile_based_boundaries(\n",
        "    california_housing_dataframe[\"households\"], 7))\n",
        "\n",
        "# Divide longitude into 10 buckets.\n",
        "longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "  longitude, boundaries=get_quantile_based_boundaries(\n",
        "    california_housing_dataframe[\"longitude\"], 10))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "U-pQDAa0MeN3",
        "colab_type": "text"
      },
      "source": [
        " ## 任务 1：使用分桶特征列训练模型\n",
        "**将我们示例中的所有实值特征进行分桶，训练模型，然后查看结果是否有所改善。**\n",
        "\n",
        "在前面的代码块中，两个实值列（即 `households` 和 `longitude`）已被转换为分桶特征列。您的任务是对其余的列进行分桶，然后运行代码来训练模型。您可以采用各种启发法来确定分桶的范围。本练习使用了分位数技巧，通过这种方式选择分桶边界后，每个分桶将包含相同数量的样本。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "YFXV9lyMLedy",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "\n",
        "  #\n",
        "  # YOUR CODE HERE: bucketize the following columns, following the example above:\n",
        "  #\n",
        "  bucketized_latitude = \n",
        "  bucketized_housing_median_age = \n",
        "  bucketized_median_income =\n",
        "  bucketized_rooms_per_person =\n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person])\n",
        "  \n",
        "  return feature_columns\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "0FfUytOTNJhL",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZTDHHM61NPTw",
        "colab_type": "text"
      },
      "source": [
        " ### 解决方案\n",
        "\n",
        "点击下方即可查看解决方案。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JQHnUhL_NRwA",
        "colab_type": "text"
      },
      "source": [
        " 您可能想知道如何确定要使用多少个分桶。这当然要取决于数据。在这里，我们只是选择了任意值，以获得一个不太大的模型。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Ro5civQ3Ngh_",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "  \n",
        "  # Divide latitude into 10 buckets.\n",
        "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
        "    latitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"latitude\"], 10))\n",
        "\n",
        "  # Divide housing_median_age into 7 buckets.\n",
        "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
        "    housing_median_age, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"housing_median_age\"], 7))\n",
        "  \n",
        "  # Divide median_income into 7 buckets.\n",
        "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
        "    median_income, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"median_income\"], 7))\n",
        "  \n",
        "  # Divide rooms_per_person into 7 buckets.\n",
        "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
        "    rooms_per_person, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"rooms_per_person\"], 7))\n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person])\n",
        "  \n",
        "  return feature_columns"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "RNgfYk6OO8Sy",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 531
        },
        "outputId": "39a95c59-2172-46ec-b39c-4373fabd113e"
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "execution_count": 10,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Training model...\n",
            "RMSE (on training data):\n",
            "  period 00 : 170.55\n",
            "  period 01 : 144.24\n",
            "  period 02 : 127.74\n",
            "  period 03 : 116.56\n",
            "  period 04 : 108.61\n",
            "  period 05 : 102.73\n",
            "  period 06 : 98.25\n",
            "  period 07 : 94.86\n",
            "  period 08 : 91.97\n",
            "  period 09 : 89.53\n",
            "Model training finished.\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nTkSuQmCC\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AFJ1qoZPlQcs",
        "colab_type": "text"
      },
      "source": [
        " ## 特征组合\n",
        "\n",
        "组合两个（或更多个）特征是使用线性模型来学习非线性关系的一种聪明做法。在我们的问题中，如果我们只使用 `latitude` 特征进行学习，那么该模型可能会发现特定纬度（或特定纬度范围内，因为我们已经将其分桶）的城市街区更可能比其他街区住房成本高昂。`longitude` 特征的情况与此类似。但是，如果我们将 `longitude` 与 `latitude` 组合，产生的组合特征则代表一个明确的城市街区。如果模型发现某些城市街区（位于特定纬度和经度范围内）更可能比其他街区住房成本高昂，那么这将是比单独考虑两个特征更强烈的信号。\n",
        "\n",
        "目前，特征列 API 仅支持组合离散特征。要组合两个连续的值（比如 `latitude` 或 `longitude`），我们可以对其进行分桶。\n",
        "\n",
        "如果我们组合 `latitude` 和 `longitude` 特征（例如，假设 `longitude` 被分到 `2` 个分桶中，而 `latitude` 有 `3` 个分桶），我们实际上会得到 6 个组合的二元特征。当我们训练模型时，每个特征都会分别获得自己的权重。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-Rk0c1oTYaVH",
        "colab_type": "text"
      },
      "source": [
        " ## 任务 2：使用特征组合训练模型\n",
        "\n",
        "**在模型中添加 `longitude` 与 `latitude` 的特征组合，训练模型，然后确定结果是否有所改善。**\n",
        "\n",
        "请参阅有关 [`crossed_column()`](https://www.tensorflow.org/api_docs/python/tf/feature_column/crossed_column) 的 TensorFlow API 文档，了解如何为您的组合构建特征列。`hash_bucket_size` 可以设为 `1000`。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "-eYiVEGeYhUi",
        "colab_type": "code",
        "cellView": "both",
        "colab": {}
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "  \n",
        "  # Divide latitude into 10 buckets.\n",
        "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
        "    latitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"latitude\"], 10))\n",
        "\n",
        "  # Divide housing_median_age into 7 buckets.\n",
        "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
        "    housing_median_age, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"housing_median_age\"], 7))\n",
        "  \n",
        "  # Divide median_income into 7 buckets.\n",
        "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
        "    median_income, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"median_income\"], 7))\n",
        "  \n",
        "  # Divide rooms_per_person into 7 buckets.\n",
        "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
        "    rooms_per_person, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"rooms_per_person\"], 7))\n",
        "  \n",
        "  # YOUR CODE HERE: Make a feature column for the long_x_lat feature cross\n",
        "  long_x_lat = \n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person,\n",
        "    long_x_lat])\n",
        "  \n",
        "  return feature_columns"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "xZuZMp3EShkM",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0i7vGo9PTaZl",
        "colab_type": "text"
      },
      "source": [
        " ### 解决方案\n",
        "\n",
        "点击下方即可查看解决方案。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "3tAWu8qSTe2v",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def construct_feature_columns():\n",
        "  \"\"\"Construct the TensorFlow Feature Columns.\n",
        "\n",
        "  Returns:\n",
        "    A set of feature columns\n",
        "  \"\"\" \n",
        "  households = tf.feature_column.numeric_column(\"households\")\n",
        "  longitude = tf.feature_column.numeric_column(\"longitude\")\n",
        "  latitude = tf.feature_column.numeric_column(\"latitude\")\n",
        "  housing_median_age = tf.feature_column.numeric_column(\"housing_median_age\")\n",
        "  median_income = tf.feature_column.numeric_column(\"median_income\")\n",
        "  rooms_per_person = tf.feature_column.numeric_column(\"rooms_per_person\")\n",
        "  \n",
        "  # Divide households into 7 buckets.\n",
        "  bucketized_households = tf.feature_column.bucketized_column(\n",
        "    households, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"households\"], 7))\n",
        "\n",
        "  # Divide longitude into 10 buckets.\n",
        "  bucketized_longitude = tf.feature_column.bucketized_column(\n",
        "    longitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"longitude\"], 10))\n",
        "  \n",
        "  # Divide latitude into 10 buckets.\n",
        "  bucketized_latitude = tf.feature_column.bucketized_column(\n",
        "    latitude, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"latitude\"], 10))\n",
        "\n",
        "  # Divide housing_median_age into 7 buckets.\n",
        "  bucketized_housing_median_age = tf.feature_column.bucketized_column(\n",
        "    housing_median_age, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"housing_median_age\"], 7))\n",
        "  \n",
        "  # Divide median_income into 7 buckets.\n",
        "  bucketized_median_income = tf.feature_column.bucketized_column(\n",
        "    median_income, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"median_income\"], 7))\n",
        "  \n",
        "  # Divide rooms_per_person into 7 buckets.\n",
        "  bucketized_rooms_per_person = tf.feature_column.bucketized_column(\n",
        "    rooms_per_person, boundaries=get_quantile_based_boundaries(\n",
        "      training_examples[\"rooms_per_person\"], 7))\n",
        "  \n",
        "  # YOUR CODE HERE: Make a feature column for the long_x_lat feature cross\n",
        "  long_x_lat = tf.feature_column.crossed_column(\n",
        "  set([bucketized_longitude, bucketized_latitude]), hash_bucket_size=1000) \n",
        "  \n",
        "  feature_columns = set([\n",
        "    bucketized_longitude,\n",
        "    bucketized_latitude,\n",
        "    bucketized_housing_median_age,\n",
        "    bucketized_households,\n",
        "    bucketized_median_income,\n",
        "    bucketized_rooms_per_person,\n",
        "    long_x_lat])\n",
        "  \n",
        "  return feature_columns"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "-_vvNYIyTtPC",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 531
        },
        "outputId": "e9954c70-15ef-4eee-ce00-25c16bff4497"
      },
      "source": [
        "_ = train_model(\n",
        "    learning_rate=1.0,\n",
        "    steps=500,\n",
        "    batch_size=100,\n",
        "    feature_columns=construct_feature_columns(),\n",
        "    training_examples=training_examples,\n",
        "    training_targets=training_targets,\n",
        "    validation_examples=validation_examples,\n",
        "    validation_targets=validation_targets)"
      ],
      "execution_count": 12,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Training model...\n",
            "RMSE (on training data):\n",
            "  period 00 : 164.49\n",
            "  period 01 : 136.31\n",
            "  period 02 : 119.19\n",
            "  period 03 : 107.94\n",
            "  period 04 : 100.10\n",
            "  period 05 : 94.34\n",
            "  period 06 : 90.00\n",
            "  period 07 : 86.49\n",
            "  period 08 : 83.67\n",
            "  period 09 : 81.36\n",
            "Model training finished.\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAacAAAEYCAYAAAD4czk4AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzs3Xd4lFX68PHvnUZIg5CEAAkQSugt\nEIoCIkVFseEq9l3sugq6urqWte2q67vr+lPXsjawLorYFUSRjkjvPUCAhJZQ0kjPef84T8IkhhRS\nZkLuz3XNlZmn3lMy95zynCPGGJRSSilP4uXuAJRSSqmyNDkppZTyOJqclFJKeRxNTkoppTyOJiel\nlFIeR5OTUkopj6PJSakGQkSMiHR2dxwNmYg8KiLvnOa+74nIM7UdkyqfJqcznIgkiki2iGSKyEHn\nHyyoFo4b43xZ+lSwzVPONveWWX6vs/ypmsZRXSIyTER+EZE0ETkqIktEZGB9x1HbRGS+iOQ473Px\n7Vt3x1UbnM9KlvOckkXkRRHxPp1jGWOeM8bcWtsxqtqnyalxuMQYEwT0A+KAR+rx3NuB35dZ9gdn\neb0SkRDgO+A/QAsgCngayHVDLKf15VqJe4wxQS63S05x7t/8oKjoR0ZVj1HH+jqf4dHAdcBt1T2A\nG2JWNaDJqRExxhwEZmOTFAAi0kxEPhCRFBHZIyJ/FREvZ52X83iPiBx2tmvm7LrQ+Xvc+UV71ilO\nuwIIEJGezjF7Av7O8hIicrGIrBWR407Jpo/LuodFZKeIZIjIZhEZ77JuoogsFpEXROSYiOwWkQtP\nEUsX53WYZowpNMZkG2N+NMasd47l7RwnVUR2icjdrqVDpxQ6xuXcT4nIRy6PP3NKp2kisrD4OTvr\n3hORN0RkpohkASNFpIlzvr0ickhE/isiTV32eVBEDojIfhG5+RTPqVIicq6IJInIX0TkIDC1vGXO\ntreJSIJTqvxGRNq4HMc4r8kOYEc555klIveUWbZORK4Q6/+cz1G6iGwQkV7VfS7GmK3AIqCXc/w2\nIvK58/ndLSKTXc79lIjMEJGPRCQdmFjOe3apiGxyPnfzRaS7y7o4EVntfO4+xX5ui9eFi8h3zn5H\nRWRR8f+Nqh36YjYiIhINXAgkuCz+D9AM6AiMwJZybnLWTXRuI531QcCrzrpznL/NnV/pSys49Yec\n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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ymlHJ-vrhLZw",
        "colab_type": "text"
      },
      "source": [
        " ## 可选挑战：尝试更多合成特征\n",
        "\n",
        "到目前为止，我们已经尝试了简单的分桶列和特征组合，但还有更多组合有可能会改进结果。例如，您可以组合多个列。如果改变分桶的数量，会出现什么情况？您还能想到哪些其他的合成特征？它们能否改进模型效果？"
      ]
    }
  ]
}